﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq.Expressions;
using IDI.Studio.Core;

namespace IDI.Studio.Data
{
    public abstract partial class Repository<TEntity> : IRepository<TEntity> where TEntity : Entity
    {
        /// <summary>
        /// Adds an entity to the repository.
        /// </summary>
        /// <param name="model">The entity to be added to the repository.</param>
        protected abstract void DoAdd(TEntity entity);

        /// <summary>
        /// Removes the entity from current repository.
        /// </summary>
        /// <param name="model">The entity to be removed.</param>
        protected abstract void DoRemove(TEntity entity);

        /// <summary>
        /// Updates the entity in the current repository.
        /// </summary>
        /// <param name="model">The entity to be updated.</param>
        protected abstract void DoUpdate(TEntity entity);

        /// <summary>
        /// Gets the entity instance from repository by a given key.
        /// </summary>
        /// <param name="key">The key of the entity.</param>
        /// <returns>The instance of the entity.</returns>
        protected abstract TEntity DoGetByKey(Guid key);

        /// <summary>
        /// Finds a single entity that matches the given specification.
        /// </summary>
        /// <param name="specification">The specification with which the entity should match.</param>
        /// <returns>The instance of the entity.</returns>
        protected abstract TEntity DoFind(ISpecification<TEntity> specification);

        /// <summary>
        /// Finds a single entity from the repository.
        /// </summary>
        /// <param name="specification">The specification with which the entity should match.</param>
        /// <param name="eagerLoadingProperties">The properties for the aggregated entities that need to be loaded.</param>
        /// <returns>The entity.</returns>
        protected abstract TEntity DoFind(ISpecification<TEntity> specification, params Expression<Func<TEntity, dynamic>>[] eagerLoadingProperties);

        /// <summary>
        /// Checkes whether the entity, which matches the given specification, exists in the repository.
        /// </summary>
        /// <param name="specification">The specification with which the entity should match.</param>
        /// <returns>True if the entity exists, otherwise false.</returns>
        protected abstract bool DoExists(ISpecification<TEntity> specification);

        /// <summary>
        /// Finds all the entities that match the given specification, and sorts the entities
        /// by using the provided sort predicate and the specified sort order.
        /// </summary>
        /// <param name="specification">The specification with which the entities should match.</param>
        /// <param name="sortPredicate">The sort predicate which is used for sorting.</param>
        /// <param name="sortOrder">The <see cref="SortOrder"/> enumeration which specifies the sort order.</param>
        /// <returns>All the entities that match the given specification and were sorted by using the given sort predicate and the sort order.</returns>
        protected abstract IEnumerable<TEntity> DoFindAll(ISpecification<TEntity> specification, Expression<Func<TEntity, dynamic>> sortPredicate, SortOrder sortOrder);

        /// <summary>
        /// Finds all the entities that match the given specification with paging enabled, and sorts the entities
        /// by using the provided sort predicate and the specified sort order.
        /// </summary>
        /// <param name="specification">The specification with which the entities should match.</param>
        /// <param name="sortPredicate">The sort predicate which is used for sorting.</param>
        /// <param name="sortOrder">The <see cref="SortOrder"/> enumeration which specifies the sort order.</param>
        /// <param name="pageNumber">The number of entities per page.</param>
        /// <param name="pageSize">The number of entities per page.</param>
        /// <returns>All the entities that match the given specification and were sorted by using the given sort predicate and the sort order.</returns>
        protected abstract PagedResult<TEntity> DoFindAll(ISpecification<TEntity> specification, Expression<Func<TEntity, dynamic>> sortPredicate, SortOrder sortOrder, int pageNumber, int pageSize);

        /// <summary>
        /// Finds all the entities from repository.
        /// </summary>
        /// <param name="specification">The specification with which the entities should match.</param>
        /// <param name="sortPredicate">The sort predicate which is used for sorting.</param>
        /// <param name="sortOrder">The <see cref="SortOrder"/> enumeration which specifies the sort order.</param>
        /// <param name="eagerLoadingProperties">The properties for the aggregated entities that need to be loaded.</param>
        /// <returns>The entity.</returns>
        protected abstract IEnumerable<TEntity> DoFindAll(ISpecification<TEntity> specification, Expression<Func<TEntity, dynamic>> sortPredicate, SortOrder sortOrder, params Expression<Func<TEntity, dynamic>>[] eagerLoadingProperties);

        /// <summary>
        /// Finds all the entities from repository.
        /// </summary>
        /// <param name="specification">The specification with which the entities should match.</param>
        /// <param name="sortPredicate">The sort predicate which is used for sorting.</param>
        /// <param name="sortOrder">The <see cref="SortOrder"/> enumeration which specifies the sort order.</param>
        /// <param name="pageNumber">The page number.</param>
        /// <param name="pageSize">The number of entities per page.</param>
        /// <param name="eagerLoadingProperties">The properties for the aggregated entities that need to be loaded.</param>
        /// <returns>The entity.</returns>
        protected abstract PagedResult<TEntity> DoFindAll(ISpecification<TEntity> specification, Expression<Func<TEntity, dynamic>> sortPredicate, SortOrder sortOrder, int pageNumber, int pageSize, params Expression<Func<TEntity, dynamic>>[] eagerLoadingProperties);

        /// <summary>
        /// Finds all the entities from repository.
        /// </summary>
        /// <returns>All the entities got from the repository.</returns>
        protected virtual IEnumerable<TEntity> DoFindAll()
        {
            return DoFindAll(new AnySpecification<TEntity>(), null, SortOrder.Unspecified);
        }

        /// <summary>
        /// Finds all the entities that match the given specification.
        /// </summary>
        /// <param name="specification">The specification with which the entities should match.</param>
        /// <returns>All the entities that match the given specification.</returns>
        protected virtual IEnumerable<TEntity> DoFindAll(ISpecification<TEntity> specification)
        {
            return DoFindAll(specification, null, SortOrder.Unspecified);
        }

        /// <summary>
        /// Finds all the entities from repository, sorting by using the provided sort predicate
        /// and the specified sort order.
        /// </summary>
        /// <param name="sortPredicate">The sort predicate which is used for sorting.</param>
        /// <param name="sortOrder">The <see cref="SortOrder"/> enumeration which specifies the sort order.</param>
        /// <returns>All the entities got from the repository, with the entities being sorted by
        /// using the provided sort predicate and the sort order.</returns>
        protected virtual IEnumerable<TEntity> DoFindAll(Expression<Func<TEntity, dynamic>> sortPredicate, SortOrder sortOrder)
        {
            return DoFindAll(new AnySpecification<TEntity>(), sortPredicate, sortOrder);
        }

        /// <summary>
        /// Finds all the entities from repository.
        /// </summary>
        /// <param name="eagerLoadingProperties">The properties for the aggregated entities that need to be loaded.</param>
        /// <returns>The entity.</returns>
        protected virtual IEnumerable<TEntity> DoFindAll(params Expression<Func<TEntity, dynamic>>[] eagerLoadingProperties)
        {
            return DoFindAll(new AnySpecification<TEntity>(), null, SortOrder.Unspecified, eagerLoadingProperties);
        }

        /// <summary>
        /// Finds all the entities from repository with paging enabled, sorting by using the provided sort predicate
        /// and the specified sort order.
        /// </summary>
        /// <param name="sortPredicate">The sort predicate which is used for sorting.</param>
        /// <param name="sortOrder">The <see cref="SortOrder"/> enumeration which specifies the sort order.</param>
        /// <param name="pageNumber">The page number.</param>
        /// <param name="pageSize">The number of entities per page.</param>
        /// <returns>All the entities got from the repository, with the entities being sorted by
        /// using the provided sort predicate and the sort order.</returns>
        protected virtual PagedResult<TEntity> DoFindAll(Expression<Func<TEntity, dynamic>> sortPredicate, SortOrder sortOrder, int pageNumber, int pageSize)
        {
            return DoFindAll(new AnySpecification<TEntity>(), sortPredicate, sortOrder, pageNumber, pageSize);
        }

        /// <summary>
        /// Finds all the entities from repository.
        /// </summary>
        /// <param name="specification">The specification with which the entities should match.</param>
        /// <param name="eagerLoadingProperties">The properties for the aggregated entities that need to be loaded.</param>
        /// <returns>The entity.</returns>
        protected virtual IEnumerable<TEntity> DoFindAll(ISpecification<TEntity> specification, params Expression<Func<TEntity, dynamic>>[] eagerLoadingProperties)
        {
            return DoFindAll(new AnySpecification<TEntity>(), null, SortOrder.Unspecified, eagerLoadingProperties);
        }

        /// <summary>
        /// Finds all the entities from repository.
        /// </summary>
        /// <param name="sortPredicate">The sort predicate which is used for sorting.</param>
        /// <param name="sortOrder">The <see cref="SortOrder"/> enumeration which specifies the sort order.</param>
        /// <param name="eagerLoadingProperties">The properties for the aggregated entities that need to be loaded.</param>
        /// <returns>The entity.</returns>
        protected virtual IEnumerable<TEntity> DoFindAll(Expression<Func<TEntity, dynamic>> sortPredicate, SortOrder sortOrder, params Expression<Func<TEntity, dynamic>>[] eagerLoadingProperties)
        {
            return DoFindAll(new AnySpecification<TEntity>(), sortPredicate, sortOrder, eagerLoadingProperties);
        }

        /// <summary>
        /// Finds all the entities from repository.
        /// </summary>
        /// <param name="sortPredicate">The sort predicate which is used for sorting.</param>
        /// <param name="sortOrder">The <see cref="SortOrder"/> enumeration which specifies the sort order.</param>
        /// <param name="pageNumber">The page number.</param>
        /// <param name="pageSize">The number of entities per page.</param>
        /// <param name="eagerLoadingProperties">The properties for the aggregated entities that need to be loaded.</param>
        /// <returns>The entity.</returns>
        protected virtual PagedResult<TEntity> DoFindAll(Expression<Func<TEntity, dynamic>> sortPredicate, SortOrder sortOrder, int pageNumber, int pageSize, params Expression<Func<TEntity, dynamic>>[] eagerLoadingProperties)
        {
            return DoFindAll(new AnySpecification<TEntity>(), sortPredicate, sortOrder, pageNumber, pageSize, eagerLoadingProperties);
        }
    }
}
